package com.kidult.android.common.view;

import com.kidult.android.common.view.FlipLoadingLayout.FlipLoadingLayoutMode;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.MeasureSpec;
import android.view.View.OnTouchListener;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.AbsListView.OnScrollListener;
import android.widget.LinearLayout.LayoutParams;

public abstract class PullToRefreshBase<T extends View> extends LinearLayout {

    public PullToRefreshBase(Context context) {
	super(context);
	init(context);
    }

    public PullToRefreshBase(Context context, AttributeSet attrs) {
	super(context, attrs);
	init(context);
    }

    public enum RefreshStatusEnum {
	/** 点击刷新 **/
	CLICK_TO_REFRESH,
	/** 拖拽刷新 **/
	PULL_TO_REFRESH,
	/** 释放刷新 **/
	RELEASE_TO_REFRESH,
	/** 刷新中 **/
	REFRESHING;
    }

    public enum PullModeEnum {
	PULL_DOWN_TO_REFRESH, PULL_UP_TO_REFRESH, BOTH, BOTH_NONE;
	boolean canPullDown() {
	    return this == PULL_DOWN_TO_REFRESH || this == BOTH;
	}

	boolean canPullUp() {
	    return this == PULL_UP_TO_REFRESH || this == BOTH;
	}
    }

    public enum CurrentPositionEnum {
	TOP, BOTTOM, MIDDLE;
    }

    protected final float DEFAULT_RESISTANCE = 3f;
    protected final int DEFAULT_HEIGHT_LIMIT = 10;
    protected final PullModeEnum DEFAULT_MODE = PullModeEnum.BOTH_NONE;
    protected final RefreshStatusEnum DEFAULT_STATE = RefreshStatusEnum.CLICK_TO_REFRESH;

    protected float mResistance = DEFAULT_RESISTANCE;
    protected int mHeightLimit = DEFAULT_HEIGHT_LIMIT;
    protected PullModeEnum mCurrentMode = DEFAULT_MODE;
    protected RefreshStatusEnum mCurrentState = DEFAULT_STATE;

    private boolean isBeginDragged = false;
    boolean isReadyForPullDown = false;
    boolean isReadyForPullUp = false;

    private OnRefreshHeaderListener mOnRefreshHeaderListener;
    private OnRefreshFooterListener mOnRefreshFooterListener;

    protected CurrentPositionEnum mCurrentPosition;
    protected FlipLoadingLayout header;
    protected FlipLoadingLayout footer;
    protected float mActionDownPointY;
    T mPullableView;

    public final float getResistance() {
	return mResistance;
    }

    public final void setResistance(float mResistance) {
	this.mResistance = mResistance;
    }

    public final int getHeightLimit() {
	return mHeightLimit;
    }

    public final void setHeightLimit(int mHeightLimit) {
	this.mHeightLimit = mHeightLimit;
    }

    public final PullModeEnum getCurrentMode() {
	return mCurrentMode;
    }

    public final void setCurrentMode(PullModeEnum mCurrentMode) {
	this.mCurrentMode = mCurrentMode;
	updateUIForMode(getContext());
    }

    public final RefreshStatusEnum getCurrentState() {
	return mCurrentState;
    }

    public final void setCurrentState(RefreshStatusEnum mCurrentState) {
	this.mCurrentState = mCurrentState;
    }

    public interface OnRefreshHeaderListener {
	public void onRefreshHeader();
    }

    public interface OnRefreshFooterListener {
	public void onRefreshFooter();
    }

    public final FlipLoadingLayout getHeader() {
	if (null == header)
	    return null;
	return header;
    }

    public final int getHeaderOriginalHeight() {
	if (null == header)
	    return 0;
	return header.mOriginalHeight;
    }

    public final FlipLoadingLayout getFooter() {
	if (null == footer)
	    return null;
	return footer;
    }

    public final int getFooterOriginalHeight() {
	if (null == footer)
	    return 0;
	return footer.mOriginalHeight;
    }

    /**
     * 点击刷新View时调用<br/>
     * 主要在list仅有少量items，无法下拉刷新只能手动点击刷新View时调用
     */
    protected final class OnClickFlipLoadingLayoutRefreshListener implements OnClickListener {

	@Override
	public void onClick(View v) {
	    if (v instanceof FlipLoadingLayout) {
		FlipLoadingLayout flip = (FlipLoadingLayout) v;
		if (FlipLoadingLayoutMode.HEADER == flip.getMode() && RefreshStatusEnum.REFRESHING != mCurrentState) {
		    onRefreshHeader();
		}
		if (FlipLoadingLayoutMode.FOOTER == flip.getMode() && RefreshStatusEnum.REFRESHING != mCurrentState) {
		    onRefreshFooter();
		}
	    }
	}
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
	mCurrentPosition = judgeCurrentPosition();
	boolean isInterceptTouch = true;
	float y1 = 0;
	float y2 = 0;
	float deltaY = 0;
	final int action = ev.getAction();
	switch (action) {
	case MotionEvent.ACTION_DOWN:
	    y1 = mActionDownPointY = ev.getY();
	    isInterceptTouch = true;
	    break;
	case MotionEvent.ACTION_MOVE:
	    y2 = ev.getY();
	    deltaY = y1 - y2;
	    if (mCurrentPosition == CurrentPositionEnum.MIDDLE) {
		isInterceptTouch = false;
	    }
	    if (mCurrentPosition == CurrentPositionEnum.TOP && deltaY < 0) {
		isReadyForPullDown = true;
		isInterceptTouch = true;
	    } else if (mCurrentPosition == CurrentPositionEnum.TOP && deltaY >= 0) {
		isReadyForPullDown = false;
		isInterceptTouch = false;
	    }
	    if (mCurrentPosition == CurrentPositionEnum.BOTTOM && deltaY > 0) {
		isReadyForPullUp = true;
		isInterceptTouch = true;
	    } else if (mCurrentPosition == CurrentPositionEnum.BOTTOM && deltaY <= 0) {
		isReadyForPullUp = false;
		isInterceptTouch = false;
	    }
	case MotionEvent.ACTION_UP:
	    isInterceptTouch = false;
	default:
	    break;
	}
	return isInterceptTouch;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
	switch (event.getAction()) {
	case MotionEvent.ACTION_MOVE:
	    if (isReadyForPullDown && !isReadyForPullUp && mCurrentMode.canPullDown()) {
		adjustHeader(event);
	    } else if (!isReadyForPullDown && isReadyForPullUp && mCurrentMode.canPullUp()) {
		adjustFooter(event);
	    } else {
		setStatusClickToRefresh();
	    }
	    break;
	case MotionEvent.ACTION_UP:
	    if (mCurrentState != RefreshStatusEnum.REFRESHING) {
		switch (mCurrentState) {
		case CLICK_TO_REFRESH:
		    setStatusClickToRefresh();
		    break;
		case RELEASE_TO_REFRESH:
		    if (mCurrentMode.canPullDown() && mCurrentPosition == CurrentPositionEnum.TOP) {
			onRefreshHeader();
		    } else if (mCurrentMode.canPullUp() && mCurrentPosition == CurrentPositionEnum.BOTTOM) {
			onRefreshFooter();
		    }
		    break;
		case PULL_TO_REFRESH:
		    setStatusClickToRefresh();
		    break;
		default:
		    break;
		}
	    }
	    break;
	default:
	    break;
	}

	return super.onTouchEvent(event);
    }

    // protected final class MyListViewTouchListener implements OnTouchListener
    // {
    // @Override
    // public boolean onTouch(View v, MotionEvent event) {
    // if (mCurrentMode == PullModeEnum.BOTH_NONE) {
    // return true;
    // }
    // if (mCurrentState == RefreshStatusEnum.REFRESHING) {
    // return true;
    // }
    // if (event.getAction() == MotionEvent.ACTION_DOWN && event.getEdgeFlags()
    // != 0) {
    // return false;
    // }
    //
    // Log.i("MyOnTouchEvent", "MyOnTouchEvent");
    // switch (event.getAction()) {
    // case MotionEvent.ACTION_DOWN:
    // if (mCurrentPosition == CurrentPositionEnum.TOP || mCurrentPosition ==
    // CurrentPositionEnum.BOTTOM) {
    // return true;
    // }
    // break;
    // case MotionEvent.ACTION_UP:
    // if (isBeginDragged) {
    // isBeginDragged = false;
    // if (mCurrentState != RefreshStatusEnum.REFRESHING) {
    // switch (mCurrentState) {
    // case CLICK_TO_REFRESH:
    // setStatusClickToRefresh();
    // break;
    // case RELEASE_TO_REFRESH:
    // if (mCurrentMode.canPullDown() && mCurrentPosition ==
    // CurrentPositionEnum.TOP) {
    // onRefreshHeader();
    // } else if (mCurrentMode.canPullUp() && mCurrentPosition ==
    // CurrentPositionEnum.BOTTOM) {
    // onRefreshFooter();
    // }
    // break;
    // case PULL_TO_REFRESH:
    // setStatusClickToRefresh();
    // break;
    // default:
    // break;
    // }
    // return true;
    // }
    // }
    // break;
    // case MotionEvent.ACTION_MOVE:
    // if (isBeginDragged) {
    // if (mCurrentMode.canPullDown() && mCurrentPosition ==
    // CurrentPositionEnum.TOP) {
    // adjustHeader(event);
    // } else if (mCurrentMode.canPullUp() && mCurrentPosition ==
    // CurrentPositionEnum.BOTTOM) {
    // adjustFooter(event);
    // }
    // return true;
    // }
    // // else {
    // // setStatusClickToRefresh();
    // // }
    // break;
    // default:
    // break;
    // }
    //
    // return false;
    // }
    // }

    /**
     * 准备刷新
     */
    private void onRefreshBegin() {
	setStatusRefreshing();
    }

    /**
     * 刷新
     */
    public final void onRefreshHeader() {
	if (mOnRefreshHeaderListener != null) {
	    onRefreshBegin();
	    mOnRefreshHeaderListener.onRefreshHeader();
	}
    }

    public final void onRefreshFooter() {
	if (mOnRefreshFooterListener != null) {
	    onRefreshBegin();
	    mOnRefreshFooterListener.onRefreshFooter();
	}
    }

    /**
     * 刷新结束
     * 
     * @param lastUpdatedText
     *            上次更新信息，若为null，不显示
     */
    public final void onRefreshComplete(CharSequence lastUpdatedText) {
	setLastUpdatedText(lastUpdatedText);
	onRefreshComplete();
    }

    /**
     * 刷新结束，恢复View状态
     */
    public final void onRefreshComplete() {
	setStatusClickToRefresh();
    }

    public T getmPullableView() {
	return mPullableView;
    }

    protected void adjustHeader(MotionEvent event) {
	int mHistorySize = event.getHistorySize();
	for (int i = 0; i < mHistorySize; i++) {
	    int y = Math.round(Math.min(mActionDownPointY - event.getHistoricalY(i), 0) / mResistance);
	    if (mCurrentState != RefreshStatusEnum.PULL_TO_REFRESH && header.mOriginalHeight >= Math.abs(y)) {
		setStatusPullToRefresh();
	    } else if (mCurrentState == RefreshStatusEnum.PULL_TO_REFRESH && header.mOriginalHeight < Math.abs(y)) {
		setStatusReleaseToRefresh();
	    }
	    scrollTo(0, y);
	}

	// int y = Math.round(Math.min(mActionDownPointY - event.getY(), 0) /
	// mResistance);
	// if (mCurrentState != RefreshStatusEnum.PULL_TO_REFRESH &&
	// header.mOriginalHeight >= Math.abs(y)) {
	// setStatusPullToRefresh();
	// } else if (mCurrentState == RefreshStatusEnum.PULL_TO_REFRESH &&
	// header.mOriginalHeight < Math.abs(y)) {
	// setStatusReleaseToRefresh();
	// }
	// scrollTo(0, y);

	// setPadding(mOriginalPaddingLeft,
	// Math.abs(Math.round(Math.min(mActionDownPointY -
	// event.getHistoricalY(i), 0) / resistance)),
	// mOriginalPaddingRight, mOriginalPaddingButtom);
	//
	// scrollTo(0, y);
	// header.adjustPadding(event, mActionDownPointY, mResistance);
    }

    protected void adjustFooter(MotionEvent event) {
	int mHistorySize = event.getHistorySize();
	for (int i = 0; i < mHistorySize; i++) {
	    int y = Math.round(Math.max(mActionDownPointY - event.getHistoricalY(i), 0) / mResistance);
	    if (mCurrentState != RefreshStatusEnum.PULL_TO_REFRESH && header.mOriginalHeight >= Math.abs(y)) {
		setStatusPullToRefresh();
	    } else if (mCurrentState == RefreshStatusEnum.PULL_TO_REFRESH && header.mOriginalHeight < Math.abs(y)) {
		setStatusReleaseToRefresh();
	    }
	    scrollTo(0, y);
	}
	// setPadding(mOriginalPaddingLeft, mOriginalPaddingTop,
	// mOriginalPaddingRight,
	// Math.round(Math.max(mActionDownPointY - event.getHistoricalY(i), 0) /
	// resistance));
	// footer.adjustPadding(event, mActionDownPointY, mResistance);
    }

    /**
     * 设置上次更新信息
     * 
     * @param lastUpdatedText
     *            上次更新信息，若为null，不显示
     */
    public final void setLastUpdatedText(CharSequence lastUpdatedText) {
	if (null != header)
	    header.setLastUpdatedText(lastUpdatedText);
    }

    /**
     * 设置刷新事件器
     * 
     * @param onRefreshListener
     */
    public final void setOnRefreshHeaderListener(OnRefreshHeaderListener onRefreshHeaderListener) {
	mOnRefreshHeaderListener = onRefreshHeaderListener;
    }

    public final void setOnRefreshFooterListener(OnRefreshFooterListener onRefreshFooterListener) {
	mOnRefreshFooterListener = onRefreshFooterListener;
    }

    protected FlipLoadingLayout createLoadingLayout(Context context, FlipLoadingLayoutMode mode) {
	FlipLoadingLayout flip = new FlipLoadingLayout(context, mode);
	measureView(flip);
	flip.initOriginalProperties();
	return flip;
    }

    private void addRefreshableView(Context context, T refreshableView) {
	FrameLayout frameLayout = new FrameLayout(context);
	frameLayout.addView(refreshableView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
	addViewInternal(frameLayout, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, 0, 1.0f));
    }

    protected void init(Context context) {
	setOrientation(LinearLayout.VERTICAL);
	mPullableView = createPullableView(context);
	addRefreshableView(context, mPullableView);
	header = createLoadingLayout(context, FlipLoadingLayoutMode.HEADER);
	footer = createLoadingLayout(context, FlipLoadingLayoutMode.FOOTER);
	Log.i("init header height", header.getHeight() + "");
	Log.i("init footer height", footer.getHeight() + "");
	updateUIForMode(context);
    }

    /**
     * 测量View的宽度和高度
     * 
     * @param child
     */
    protected void measureView(View child) {
	ViewGroup.LayoutParams p = child.getLayoutParams();
	if (p == null) {
	    p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
	}

	int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);
	int lpHeight = p.height;
	int childHeightSpec;
	if (lpHeight > 0) {
	    childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
	} else {
	    childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
	}
	child.measure(childWidthSpec, childHeightSpec);
    }

    protected void setStatusClickToRefresh() {
	reSetScroll();
	if (mCurrentState != RefreshStatusEnum.CLICK_TO_REFRESH) {
	    switch (mCurrentPosition) {
	    case TOP:
		header.setStatusClickToRefresh();
		break;
	    case BOTTOM:
		footer.setStatusClickToRefresh();
		break;
	    default:
		break;
	    }
	    mCurrentState = RefreshStatusEnum.CLICK_TO_REFRESH;
	}
    }

    protected void setStatusPullToRefresh() {
	if (mCurrentState != RefreshStatusEnum.PULL_TO_REFRESH) {
	    switch (mCurrentPosition) {
	    case TOP:
		header.setStatusPullToRefresh();
		break;
	    case BOTTOM:
		footer.setStatusPullToRefresh();
		break;
	    default:
		break;
	    }
	    mCurrentState = RefreshStatusEnum.PULL_TO_REFRESH;
	}
    }

    protected void setStatusReleaseToRefresh() {
	if (mCurrentState != RefreshStatusEnum.RELEASE_TO_REFRESH) {
	    switch (mCurrentPosition) {
	    case TOP:
		header.setStatusReleaseToRefresh();
		break;
	    case BOTTOM:
		footer.setStatusReleaseToRefresh();
		break;
	    default:
		break;
	    }
	    mCurrentState = RefreshStatusEnum.RELEASE_TO_REFRESH;
	}
    }

    protected void setStatusRefreshing() {
	reSetScroll();
	if (mCurrentState != RefreshStatusEnum.REFRESHING) {
	    switch (mCurrentPosition) {
	    case TOP:
		header.setStatusRefreshing();
		break;
	    case BOTTOM:
		footer.setStatusRefreshing();
		break;
	    default:
		break;
	    }
	    mCurrentState = RefreshStatusEnum.REFRESHING;
	}
    }

    private void reSetScroll() {
	this.scrollTo(0, 0);
    }

    /**
     * Used internally for adding view. Need because we override addView to
     * pass-through to the Refreshable View
     */
    protected final void addViewInternal(View child, int index, ViewGroup.LayoutParams params) {
	super.addView(child, index, params);
    }

    /**
     * Used internally for adding view. Need because we override addView to
     * pass-through to the Refreshable View
     */
    protected final void addViewInternal(View child, ViewGroup.LayoutParams params) {
	super.addView(child, -1, params);
    }

    protected void updateUIForMode(Context context) {
	if (null != header && this == header.getParent()) {
	    removeView(header);
	}
	if (mCurrentMode.canPullDown()) {
	    header = createLoadingLayout(context, FlipLoadingLayoutMode.HEADER);
	    addViewInternal(header, 0, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
		    ViewGroup.LayoutParams.WRAP_CONTENT));
	}

	// Remove Footer, and then add Footer Loading View again if needed
	if (null != footer && mPullableView == footer.getParent()) {
	    removeView(footer);
	}
	if (mCurrentMode.canPullUp()) {
	    footer = createLoadingLayout(context, FlipLoadingLayoutMode.FOOTER);
	    addViewInternal(footer, -1, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
		    ViewGroup.LayoutParams.WRAP_CONTENT));
	}
	refreshLoadingViewsHeight();
    }

    private void refreshLoadingViewsHeight() {
	// Hide Loading Views
	switch (mCurrentMode) {
	case BOTH_NONE:
	    setPadding(0, 0, 0, 0);
	case BOTH:
	    setPadding(0, -header.mOriginalHeight, 0, -footer.mOriginalHeight);
	    break;
	case PULL_UP_TO_REFRESH:
	    setPadding(0, 0, 0, -footer.mOriginalHeight);
	    break;
	case PULL_DOWN_TO_REFRESH:
	default:
	    setPadding(0, -header.mOriginalHeight, 0, 0);
	    break;
	}
    }

    protected abstract CurrentPositionEnum judgeCurrentPosition();

    protected abstract T createPullableView(Context context);
}